<?php
class DBase {
	private $db = null;
	private $driver = "sqlite";
	private $hostname = null;
	private $uniqHostID = null;
	public $dbFileName = "mysqlitedb.sq3";
	public $s3Bucket = null;
	private $uniqBucketID = null;
	
	const Err_Cant_Create_DbaseFile = "Can't create dbase file";
	const Err_Cant_Create_DbaseFile_code = 100;
	
	const Err_Cant_Get_Hostname = "Can't get host name";
	const Err_Cant_Get_Hostname_code = 102;
	
	const Err_DB_Error = "DBase returned error";
	const Err_DB_Error_code = 103;
	
	const Err_S3Bucket_Not_Set = "S3Bucket Not Set";
	const Err_S3Bucket_Not_Set_code = 104;
	
	public function __construct($opts = null) {
		if (!is_null($opts) && is_array($opts)) {
			foreach ($opts as $key => $value) {
				switch ($key) {
					case "dbFileName":
						$this->dbFileName = $value;
						break;
					case "driver":
						$this->driver = $value;
						break;
					case "hostname":
						$this->hostname = $value;
						break;
					case "s3Bucket":
						$this->s3Bucket = $value;
						break;
					default:
						throw new Exception ("Unknown key in options: '$key'");
						break;
				}
			}
		}
		if (is_null($this->hostname)) {
			$this->hostname = gethostname();
			if (!$this->hostname || is_null($this->hostname)) {
				throw new Exception (DBase::Err_Cant_Get_Hostname, DBase::Err_Cant_Get_Hostname_code);
			}
		}
		if (is_null($this->s3Bucket)) {
			throw new Exception (DBase::Err_S3Bucket_Not_Set, DBase::Err_S3Bucket_Not_Set_code);
		}
		if (!file_exists($this->dbFileName)) {
			if (touch($this->dbFileName) === false) {
				throw new Exception (DBase::Err_Cant_Create_DbaseFile . " '" . $this->dbFileName ."'", DBase::Err_Cant_Create_DbaseFile_code);
			}
		}
		try {
			$this->db = new PDO($this->driver . ":" . $this->dbFileName, null, null, array(PDO::ATTR_PERSISTENT => true));
		} catch (PDOException $e) {
			throw new Exception ($e->getMessage, $e->getCode);
		}
		$this->sanity();
	}
	
	private function sanity() {
		$create = array(
			"CREATE TABLE IF NOT EXISTS hosts (uuid TEXT NOT NULL, hostname TEXT NOT NULL, UNIQUE (hostname, uuid) ON CONFLICT FAIL)",
			"CREATE TABLE IF NOT EXISTS buckets (uuid TEXT NOT NULL, bucket TEXT NOT NULL, UNIQUE (bucket) ON CONFLICT FAIL)",
			"CREATE TABLE IF NOT EXISTS objects (uuid TEXT NOT NULL, objectName TEXT NOT NULL, bucketID TEXT NOT NULL, UNIQUE (objectName, bucketID) ON CONFLICT IGNORE)",
			"CREATE TABLE IF NOT EXISTS versions (objectUUID TEXT NOT NULL, versionID TEXT NOT NULL, isLatest INTEGER NOT NULL, lastModified INTEGER NOT NULL, etag TEXT NOT NULL, size INTEGER NOT NULL, owner TEXT NOT NULL, storage TEXT NOT NULL, cryptID TEXT NOT NULL, UNIQUE (versionID) ON CONFLICT REPLACE)",
			"CREATE TABLE IF NOT EXISTS files (uuid TEXT NOT NULL, checkinTime INT NOT NULL, fqpn TEXT NOT NULL, hostID TEXT NOT NULL, size INT NOT NULL, mtime INT NOT NULL, ctime INT NOT NULL, objectName TEXT NOT NULL, UNIQUE (fqpn, hostID) ON CONFLICT REPLACE)",
			"CREATE TABLE IF NOT EXISTS fileObjectLink (fileUUID NOT NULL, objectUUID NOT NULL, UNIQUE (fileUUID, objectUUID) ON CONFLICT IGNORE)");
		foreach ($create as $sql) {
			$res = $this->__exec($sql);
		}				
		// validate host		
		$res = $this->db->query('SELECT * FROM hosts WHERE hostname = "'. $this->hostname .'"');
		foreach ($res as $row) {
			if (isset($row["uuid"])) {
				$this->uniqHostID = $row["uuid"];
			}
		}
		
		if (is_null($this->uniqHostID)) {
			$this->uniqHostID = uniqid();
			$insert = "INSERT INTO hosts (uuid, hostname) VALUES (:uuid, :host)";
			$stmt = $this->db->prepare($insert);
			$stmt->bindParam(':uuid', $this->uniqHostID);
			$stmt->bindParam(':host', $this->hostname);
			$stmt->execute();
		}
		
		// validate s3Bucket
		$res = $this->db->query('SELECT * FROM buckets WHERE bucket = "'. $this->s3Bucket .'"');
		foreach ($res as $row) {
			if (isset($row["uuid"])) {
				$this->uniqBucketID = $row["uuid"];
			}
		}
		
		if (is_null($this->uniqBucketID)) {
			$this->uniqBucketID = uniqid();
			$insert = "INSERT INTO buckets (uuid, bucket) VALUES (:uuid, :bucket)";
			$stmt = $this->db->prepare($insert);
			$stmt->bindParam(':uuid', $this->uniqBucketID);
			$stmt->bindParam(':bucket', $this->s3Bucket);
			$stmt->execute();
		}
	}	
	
	public function checkinLocalFile($fileStruct) {
		/*
		CREATE TABLE IF NOT EXISTS files (
			uuid TEXT NOT NULL, 
			checkinTime INT NOT NULL, 
			fqpn TEXT NOT NULL, 
			hostID TEXT NOT NULL, 
			size INT NOT NULL, 
			mtime INT NOT NULL, 
			ctime INT NOT NULL, 
			objectName TEXT NOT NULL, 
			UNIQUE (fqpn) ON CONFLICT REPLACE)
		*/
		foreach ($fileStruct as $fqpn => $struct) {
			$uniqID = uniqid();
			$now = time();
			$insert = "INSERT INTO files (uuid, checkinTime, fqpn, hostID, size, mtime, ctime, objectName) VALUES (:uuid, :checkinTime, :fqpn, :hostID, :size, :mtime, :ctime, :objectName)";
			$stmt = $this->db->prepare($insert);
			$stmt->bindParam(':uuid', $uniqID);
			$stmt->bindParam(':checkinTime', $now);
			$stmt->bindParam(':fqpn', $fqpn);
			$stmt->bindParam(':hostID', $this->uniqHostID);
			$stmt->bindParam(':size', $struct["size"]);
			$stmt->bindParam(':mtime', $struct["mtime"]);
			$stmt->bindParam(':ctime', $struct["ctime"]);
			$stmt->bindParam(':objectName', $struct["hmac"]);
			$stmt->execute();
		}
	}
	
	public function checkinS3File($fileStruct) {
		//$uniqBucketID
		foreach ($fileStruct as $s3Object) {
			try {
				// insert the object
				$insert = "INSERT INTO objects (uuid, objectName, bucketID) VALUES (:uuid, :objectName, :bucketID)";
				$stmt = $this->db->prepare($insert);
				$stmt->bindParam(':uuid', $s3Object["objectNameHash"]);
				$stmt->bindParam(':objectName', $s3Object["objectName"]);
				$stmt->bindParam('bucketID', $this->uniqBucketID);
				$stmt->execute();
				
				// insert the version
				$isLatest = ($s3Object["isLatest"] == true) ? 1 : 0;
				$cryptID = (is_null($s3Object["cryptID"])) ? "NULL" : $s3Object["cryptID"];
				$insert = "INSERT INTO versions (objectUUID, versionID, isLatest, lastModified, etag, size, owner, storage, cryptID) VALUES (:objectUUID, :versionID, :isLatest, :lastModified, :etag, :size, :owner, :storage, :cryptID)";
				$stmt = $this->db->prepare($insert);
				$stmt->bindParam(':objectUUID', $s3Object["objectNameHash"]);
				$stmt->bindParam(':versionID', $s3Object["versionID"]);
				$stmt->bindParam(':isLatest', $isLatest);
				$stmt->bindParam(':lastModified', $s3Object["lastModified"]);
				$stmt->bindParam(':etag', $s3Object["etag"]);
				$stmt->bindParam(':size', $s3Object["size"]);
				$stmt->bindParam(':owner', $s3Object["owner"]);
				$stmt->bindParam(':storage', $s3Object["storage"]);
				$stmt->bindParam(':cryptID', $cryptID);
				if (!$stmt->execute()) {
					throw new Exception ("failed to exec versions statement");
				}
			} catch(PDOException $e) {
				throw new Exception ($e->getMessage());
			}
		}
	}
	
	private function __exec($sql) {
		$res = $this->db->exec($sql);
		if ($res === false) {
			$error = $this->db->errorInfo();
			throw new Exception (DBase::Err_DB_Error . " ({$error[0]}) ({$error[1]}): {$error[2]} for query: $sql", DBase::Err_DB_Error_code);
		}
		return $res;
	}
}
?>